package leoner.multithread5;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import leoner.multithread2.MockService;

/**
 * 目前的想法是这样,每次我们都会在我们需要添加多线程的地方注入taskExecutor这个对象,
 * 然后我们需要把所有的方法封装成为一个action这样才能传递出去。
 * 问题1：如何把多个方法传递进去,目前想到的比较友好的方式是通过一个方法,返回一个builder，
 * 然后这个东西能持续添加我们需要执行的方法任务,然后我们在调用Builder的一个方法，去执行我们真正的任务
 * 然后我们调用某个方法
 * @author kanghui
 * @param <T>
 *
 */
public class TaskExecutor<T> {
	private int size;
	private ExecutorService pool = Executors.newFixedThreadPool(3);
	
	public void doExecution(){
		
	}
	public static<T> MethodInvokeBuilder<T> getMethodBuilder(Class returnType,Object target,String targetMethodName){
		return new MethodInvokeBuilder(returnType,target,targetMethodName);
	}
	public static void main(String[] args) {
		
		MockService service = new MockServiceImpl();
		MethodInvokeBuilder<String> builder = TaskExecutor.getMethodBuilder(String.class, service, "m1");
		builder.add("m1",1, "kang").add("m2",2, "hui");
		builder.invoke();
		System.out.println("-1--------------->");
		Object object1 = builder.getObject();
		System.out.println("a--------------->"+object1);
//		System.out.println(object1.getKey());
//		System.out.println(object1.getResult());
		Object object2 = builder.getObject();
		System.out.println("b--------------->"+object2);
//		System.out.println(object2.getKey());
//		System.out.println(object2.getResult());
		
	}
	static class MethodInvokeBuilder<T>{
		/**
		 * 这个主要是用来构造我们的方法执行的action,我们拿到所有的可以执行的方法，然后去执行
		 */
		private CompletionService cs;
		//这个返回类型是可变的,要不可以直接根据generic来fixed type
		private Class returnType;
		private Object target;
		private String targetMethodName;
		private List<MethodInvokeInfoBean> methods = new ArrayList<MethodInvokeInfoBean>();
		public MethodInvokeBuilder(Class returnType,Object target,String targetMethodName){
			//为什么会有这个构造函数呢？因为现在我们调用的是一个方法,然后只是每次调用这个方法传递不同的参数而已,所以
			//对这个进行了简化,方便调用,以后会根据不同的情况添加不同的构造函数,可能还要改的更多,等到后来在考虑吧
			this.returnType = returnType;
			this.target = target;
			this.targetMethodName = targetMethodName;
			//TODO 需要看看怎么来引用这个threadPool
			this.cs = new ExecutorCompletionService<String>(Executors.newFixedThreadPool(3));
		}
		public MethodInvokeBuilder add(String key,Object... parameters){
			MethodInvokeInfoBean bean = new MethodInvokeInfoBean(this.returnType,this.target,this.targetMethodName);
			bean.setKey(key);
			bean.setParameters(parameters);
			this.methods.add(bean);
			return this;
		}
		public void invoke(){
			System.out.println(this.methods.size());
			Map<String,Future<String>> futures = new HashMap<String,Future<String>>(this.methods.size());
			Map<String,Object> results = new HashMap<String,Object>(futures.size());
			for(MethodInvokeInfoBean method : methods){
				System.out.println("11111111111111111111");
//				futures.put(entry.getValue().getKey(), cs.submit(new Task(entry.getValue())));
				cs.submit(new Task1(method,results), method.getKey());
			}
		}
		public T getObject(){
			Object obj = null;
			try {
				System.out.println("-2--------------->");
				obj = cs.take().get();
				System.out.println("-3--------------->");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return (T)obj;
		}
	}
	static class KeyObject<T>{
		private String key;
		private T result;
		public KeyObject(String key,T result){
			this.key = key;
			this.result = result;
		}
		public String getKey() {
			return key;
		}
		public void setKey(String key) {
			this.key = key;
		}
		public T getResult() {
			return result;
		}
		public void setResult(T result) {
			this.result = result;
		}
		
	}
	static class MethodInvokeInfoBean {
		private Class returnType;
		private Object targetObject;
		private String targetMethodName;
		private Object[] parameters;
		private Class[] cls;
		private String key;
		
		public MethodInvokeInfoBean(Class returnType,Object target,String targetMethodName,Object... parameters){
			this.returnType = returnType;
			this.targetObject = target;
			this.targetMethodName = targetMethodName;
			this.parameters = parameters;
			cls = new Class[this.parameters.length];
			for(int i=0; i<this.parameters.length;i++){
				cls[i] = this.parameters[i].getClass();
			}
		}
		public MethodInvokeInfoBean(Class returnType,Object target,String targetMethodName){
			this.returnType = returnType;
			this.targetObject = target;
			this.targetMethodName = targetMethodName;
		}
		public void setParameters(Object ...parameters){
			cls = new Class[parameters.length];
			for(int i=0; i<parameters.length;i++){
				cls[i] = parameters[i].getClass();
			}
			this.parameters = cls;
		}
		public void setKey(String key){
			this.key = key;
		}
		public String getKey(){
			return this.key;
		}
		public Object getTargetObject() {
			return targetObject;
		}
		public String getTargetMethodName() {
			return targetMethodName;
		}
		public Object[] getParameters() {
			return parameters;
		}
		public Class[] getCls() {
			return cls;
		}
		public Class getReturnType() {
			return returnType;
		}
	}
	/**
	 * 选择runnable的理由,因为我们现在想利用completionService的谁先执行完谁先返回的特点,但是我们无法得知我们返回
	 * 的对象是那个线程返回的,因为我们返回的Future里面没有包含返回对象的信息,更重要的是因为我们返回的对象其实是一样
	 * 的,只是根据不同的参数来获取对应的信息
	 * 所以我现在准备尝试选用Runnable,然后我们可以在初始化的时候把我们需要返回的内容放到一个Map里面，而这个map则是我们
	 * 是我们在构造这个runnable的时候传递进去的。
	 * @author kanghui
	 *
	 */
	static class Task1 implements Runnable{
		private MethodInvokeInfoBean methodInfo;
		private Map<String,Object> maps;
		public Task1(MethodInvokeInfoBean methodInfo,Map<String,Object> maps){
			this.methodInfo = methodInfo;
			this.maps = maps;
		}
		public void run() {
			System.out.println("task1 run");
			Object returnValue = null;
			try {
				Method method = methodInfo.getTargetObject().getClass().
					getMethod(methodInfo.getTargetMethodName(),methodInfo.getCls());
				
				returnValue = method.invoke(methodInfo.getTargetObject(), methodInfo.getParameters());
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.printf( "%s done invoke result:\n", returnValue );
			maps.put(methodInfo.getTargetMethodName(), returnValue);
			this.maps.put(methodInfo.getKey(), returnValue);
		}
	}
	class Task<T> implements Callable<T>{
		private MethodInvokeInfoBean methodInfo;
		private Map<String,Object> maps;
		public Task(MethodInvokeInfoBean methodInfo){
			this.methodInfo = methodInfo;
		}
		@Override
		public T call() throws Exception {
			Object returnValue = null;
			try {
				Method method = methodInfo.getTargetObject().getClass().
					getMethod(methodInfo.getTargetMethodName(),methodInfo.getCls());
				
				returnValue = method.invoke(methodInfo.getTargetObject(), methodInfo.getParameters());
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.printf( "%s done invoke result:\n", returnValue );
			maps.put(methodInfo.getTargetMethodName(), returnValue);
			return (T)returnValue;
		}
	}

}
